local tabletool = require "tabletool"
local timetool = require "timetool"
local msghelper = require "tablehelper"
local timer = require "timer"
local filelog = require "filelog"
local skynet = require "skynet"
local math_floor = math.floor
local math_ceil = math.ceil

local json = require "cjson"
json.encode_sparse_array(true,1,1)

require "dzpkenum"

local gamelogic
local seatlogic
local dzlogic
local TableLogic = {}

function TableLogic.init(tablebase, conf)
	if conf == nil then
		filelog.sys_error("TableLogic.init conf = nil")
		return false
	end
	local table_data = tablebase.table_data
	gamelogic = tablebase.logic.gamelogic
	seatlogic = tablebase.logic.seatlogic

	table_data.idx = conf.idx
	table_data.state = ETableState.TABLE_STATE_WAIT_ALL_READY

	local seat
	local count = 1
    while count <= conf.max_player_num do
    	seat = {
    		index = 0,
			rid = 0,
			state = 0,  --该坐位玩家状态
			playerinfo = {
				rolename = "",
				logo = "",
				sex = 0,
				ip_addr = "",
				fangka = 0,
				diamond = 0,
			},
			is_tuoguan = EBOOL.FALSE,
			is_ready = EBOOL.FALSE,
    		--Add 座位其他变量
    		timeout_count = 0, --超时次数
			win = 0,             ----表示玩家胜利还是失败
			diamond = 0,
			startdiamond = 0,
			fangka = 0,
    		ready_timer_id = -1, ----准备倒计时定时器
    		ready_to_time = 0,   ----准备到期时间
			cards = {},		     ----玩家手牌
			balance_score = 0,	 ----最终结算分数
			card_form = 0,   	 --牌型
			form_cards = {}, 	 --牌型对应的牌
			balance_score = 0,	 ----最终结算分数
			bets = 0,
			curr_bets = 0;
    	}
    	seatlogic.init(seat, count)
    	table.insert(table_data.seats, seat) 
		count = count + 1
    end
	table_data.conf = tabletool.deepcopy(conf)
	table_data.playernum = 0 --玩家数量

	gamelogic.init(tablebase)
	return true
end

function TableLogic.checkallonline(table_data)
	local online = false
	for _, seat in ipairs(table_data.seats) do
		if seat.state ~= ESeatState.SEAT_STATE_NO_PLAYER then
			if not seat.is_disconnect then
				online = true
				break
			end
		end
	end
	if not online then
		if table_data.timer_id > 0 then
			timer.cleartimer(table_data.timer_id)
			table_data.timer_id = -1
		end
		for _, seat in ipairs(table_data.seats) do
			if seat.state ~= ESeatState.SEAT_STATE_NO_PLAYER then
				TableLogic.passive_standuptable(table_data, seat)
			end
		end
	end
	return online
end

function TableLogic.disconnect(table_data, request, seat)
	if not table_data.conf.match_type and (seat.state == ESeatState.SEAT_STATE_WAIT_READY or seat.state == ESeatState.SEAT_STATE_WATCHING) then
		TableLogic.passive_standuptable(table_data, seat)
	else
		if table_data.conf.room_type == ERoomType.ROOM_TYPE_SYSTEM then
			TableLogic.passive_standuptable(table_data, seat)
		else
			seat.is_tuoguan = EBOOL.TRUE
			seat.is_disconnect = true
			if TableLogic.checkallonline(table_data) then
				msghelper:sendmsg_to_alltableplayer("disconnectN", {
					rid = seat.rid,
					seat_index = seat.index,
					is_tuoguan = seat.is_tuoguan,
				})
			end
		end
	end
end

function TableLogic.clear(table_data)
	if table_data.timer_id > 0 then
		timer.cleartimer(table_data.timer_id)
		table_data.timer_id = -1
	end

	if table_data.delete_table_timer_id > 0 then
		timer.cleartimer(table_data.delete_table_timer_id)
		table_data.delete_table_timer_id = -1
	end

	for k,v in pairs(table_data) do
		table_data[k] = nil
	end
end

--[[
	seat: nil表示否， 非nil表示是
--]]
function TableLogic.entertable(table_data, request, seat)
	if table_data.delete_table_timer_id and table_data.delete_table_timer_id > 0 then
		timer.cleartimer(table_data.delete_table_timer_id)
		table_data.delete_table_timer_id = -1
	end
	local rid = request.rid
	table_data.passiveleaveall = nil
	if seat then 
		if seat.is_tuoguan == EBOOL.TRUE then
			seat.is_tuoguan = EBOOL.FALSE
		end
	else
		if table_data.waits[rid] == nil then
			table_data.waits[rid] = {}
			table_data.waits[rid].playerinfo = {}
			table_data.cur_watch_playernum = table_data.cur_watch_playernum + 1			 
		end
		table_data.waits[rid].rid = rid
		table_data.waits[rid].playerinfo.rolename = request.playerinfo.rolename
		table_data.waits[rid].playerinfo.logo = request.playerinfo.rolename
		table_data.waits[rid].playerinfo.sex = request.playerinfo.sex
		if table_data.conf.match_type then
			table_data.waits[rid].waitreenter = 1
		end
	end
end

function TableLogic.reentertable(table_data, request, seat)
	seat.waitreenter = nil
	table_data.passiveleaveall = nil
	msghelper:sendmsg_to_alltableplayer("disconnectN", {
		rid = seat.rid,
		seat_index = seat.index,
		is_tuoguan = EBOOL.FALSE,
	})

	if table_data.delete_table_timer_id and table_data.delete_table_timer_id > 0 then
		timer.cleartimer(table_data.delete_table_timer_id)
		table_data.delete_table_timer_id = -1
	end

	seat.sametabledata = request.countdata
	msghelper:sendmsg_to_alltableplayer("sametablecountN", {
		rid = seat.rid,
		countdata = seat.sametabledata
	})

	if not TableLogic.is_onegameend(table_data) then
		--通知玩家当前该他操作
		if table_data.action_seat_index == 0 then return end
		table_data.action_to_time = timetool.get_time() + table_data.conf.action_timeout
		gamelogic.todonotice()
	end
end

function TableLogic.sitdowntable(table_data, rid, rdata, request, seat)
	if table_data.waits[rid] then
		table_data.waits[rid] = nil
		table_data.cur_watch_playernum = table_data.cur_watch_playernum - 1
	end
	if table_data.conf.match_type then
		seat.waitreenter = 1
	end

	seat.state = ESeatState.SEAT_STATE_WAIT_READY	--等待开始
	seat.rid = rid
	seat.playerinfo.rolename = rdata.info.rolename
	seat.playerinfo.logo = rdata.info.logo
	seat.playerinfo.sex = rdata.info.sex
	seat.playerinfo.ip_addr = rdata.info.ip_addr or ''
	seat.playerinfo.gps_addr = rdata.info.gps_addr or ''
	seat.playerinfo.fangka = rdata.money.fangka or 0
	seat.playerinfo.diamond = rdata.money.diamond or 0
	seat.playerinfo.coin = rdata.money.coin or 0
	seat.playerinfo.introduce = rdata.info.intro or ''
	seat.playerinfo.logoframe = rdata.info.logoframe
	seat.getfangka = rdata.money.fangka
	seat.diamond = rdata.money.diamond
	seat.startdiamond = seat.diamond
	-- seat.diamond = table_data.conf.min_carry_diamond
	-- seat.startdiamond = seat.diamond
	if table_data.conf.room_type == ERoomType.ROOM_TYPE_FRIEND then
		seat.diamond = 0
		seat.startdiamond = 0
	elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_SYSTEM then
		seat.diamond = 10000
		seat.startdiamond = 10000
	elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
		seat.diamond = rdata.money.coin
		seat.startdiamond = rdata.money.coin
	end

	-- local outoftimermsg = {
	-- 	rid = seat.rid,
	-- 	seat_index = seat.index,
	-- }
	-- seat.ready_timer_id = timer.settimer(table_data.conf.ready_timeout*100, "doready", outoftimermsg)
	-- seat.ready_to_time = timetool.get_time() + table_data.conf.ready_timeout

	if not table_data.conf.match_type or table_data.round > 1 then
		local noticemsg = {
			rid = seat.rid,
			seat = {},
			player_info = {},
		}
		msghelper:copy_seatinfo(noticemsg.seat, seat)
		msghelper:copy_playerinfo(noticemsg.player_info, seat)
		msghelper:sendmsg_to_alltableplayer("sitdowntableN", noticemsg)
	
		local doreadyntcmsg = {
			rid = seat.rid,
			seat_index = seat.index,
			ready_to_time = timetool.get_time() + table_data.conf.ready_timeout,
			is_ready = EBOOL.FALSE,
		}
		msghelper:sendmsg_to_alltableplayer("doreadyN", doreadyntcmsg)
	end

	if seat.is_tuoguan == EBOOL.TRUE then
		seat.is_tuoguan = EBOOL.FALSE
		--TO ADD 添加托管处理
	end
	if seat.rid > 0 and seat.rid < 100 then
		seat.is_tuoguan = EBOOL.TRUE
	end
	--增加桌子人数计数 
	table_data.sitdown_player_num = table_data.sitdown_player_num + 1
	table_data.playernum = table_data.playernum + 1
	if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
		msghelper:sendmsg_to_roomsvr("update_table_player", table_data.conf.idx, table_data.sitdown_player_num, table_data.conf.is_public)
	end
end

--
--处理重新匹配
--
function TableLogic.restart_matchtable(table_data, rid)
	local rids = {}
	local ts = timetool.get_time()
	for k, v in ipairs(table_data.seats) do
		if v.rid > 100 and v.rid ~= rid and v.state == ESeatState.SEAT_STATE_WAIT_START then
			if not v.ready_to_time or (v.ready_to_time == 0) or ts <= (v.ready_to_time - 1) then
				table.insert(rids, v.rid)
			end
		end
	end
	local match_type = table_data.conf.match_type
	if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
		msghelper:callmsg_to_roomsvr("restart_matchtable", rids, match_type)
	elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
		msghelper:callmsg_to_roomsvr("restart_coinmatchtable", rids, match_type)
	end
	for _, rid in ipairs(rids) do
		local seat = TableLogic.get_seat_by_rid(table_data, rid)
		TableLogic.passive_standuptable(table_data, seat, true)
	end
end

function TableLogic.leaveallplayers(table_data)
	for _, seat in ipairs(table_data.seats) do
		if seat.state ~= ESeatState.SEAT_STATE_NO_PLAYER then
			TableLogic.passive_standuptable(table_data, seat)
		end
	end
end

function TableLogic.leavetable(table_data, request, seat, re)
	local leavetablentc = {rid = request.rid}
	msghelper:sendmsg_to_alltableplayer("leavetableN", leavetablentc)
	msghelper:sendmsg_to_roomsvr("playerleavetable", request.rid, table_data.conf.game_room_type)

	--匹配场
	if table_data.conf.match_type and request.rid > 100 then
		if not re and not table_data.passiveleaveall then
			TableLogic.restart_matchtable(table_data, request.rid) --匹配场，将座位上玩家，加入匹配队列
		end
    end
	if (table_data.playernum <= 0 or table_data.conf.room_type == ERoomType.ROOM_TYPE_SYSTEM)
			and (not table_data.delete_table_timer_id or table_data.delete_table_timer_id == -1) then
		local retain_time = table_data.conf.retain_time
		if table_data.conf.match_type then
			retain_time = 1
		end
		table_data.delete_table_timer_id = timer.settimer(retain_time*100, "delete_table")
		table_data.round = 1
		if table_data.timer_id and table_data.timer_id > 0 then
			timer.cleartimer(table_data.timer_id)
			table_data.timer_id = -1
		end
		for k,v in ipairs(table_data.seats) do
			if v.rid ~= 0 and (v.state == ESeatState.SEAT_STATE_WAIT_READY) then
				if v.ready_timer_id > 0 then
					timer.cleartimer(v.ready_timer_id)
					v.ready_timer_id = -1
				end
			end
		end
	end
	if table_data.waits[request.rid] then
		table_data.waits[request.rid] = nil
		table_data.cur_watch_playernum = table_data.cur_watch_playernum - 1
	end
end

function TableLogic.passive_standuptable(table_data, seat, re)
	if not seat or seat.rid <= 0 or seat.standup then return end
	local rid = seat.rid
	table_data.sitdown_player_num = table_data.sitdown_player_num - 1
	table_data.playernum = table_data.playernum - 1
	if table_data.sitdown_player_num < 0 then
		table_data.sitdown_player_num = 0
		table_data.playernum = 0
		filelog.sys_error("passive_standuptable: sitdown_player_num < 0 !!!", table_data.sitdown_player_num)
	end
	if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
		msghelper:sendmsg_to_roomsvr("update_table_player", table_data.conf.idx, table_data.sitdown_player_num, table_data.conf.is_public)
	end
	local noticemsg = {
		rid = rid, 
		seat_index = seat.index,
		state = seat.state,
	}
	msghelper:sendmsg_to_alltableplayer("standuptableN", noticemsg) --同知座位上的人，某位玩家离
	seat.standup = true

	table_data.winner = nil

	TableLogic.leavetable(table_data, {rid = rid}, seat, re)
	--初始化座位数据
	seatlogic.clear_seat(seat)
end

function TableLogic.standuptable(table_data, request, seat)
	if not seat or seat.rid <= 0 then return end
	table_data.sitdown_player_num = table_data.sitdown_player_num - 1
	table_data.playernum = table_data.playernum - 1
	if table_data.sitdown_player_num < 0 then
		table_data.sitdown_player_num = 0
		table_data.playernum = 0
		filelog.sys_error("standuptable: sitdown_player_num < 0 !!!", table_data.sitdown_player_num)
	end
	if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
		msghelper:sendmsg_to_roomsvr("update_table_player", table_data.conf.idx, table_data.sitdown_player_num, table_data.conf.is_public)
	end
	local noticemsg = {
		rid = seat.rid, 
		seat_index = seat.index,
		state = seat.state,
	}
	msghelper:sendmsg_to_alltableplayer("standuptableN", noticemsg)
	seat.standup = true
	-- if seat.index == table_data.winner then
		table_data.winner = nil
	-- end
	--初始化座位数据
	seatlogic.clear_seat(seat)
end

function TableLogic.gameready(table_data, request, seat)
	if seat.ready_timer_id and seat.ready_timer_id > 0  then ---取消准备倒计时
		timer.cleartimer(seat.ready_timer_id)
		seat.ready_timer_id = -1
		seat.ready_to_time = 0
	end

	seat.state = ESeatState.SEAT_STATE_WAIT_START
	seat.is_ready = EBOOL.TRUE
	local noticemsg = {
		rid = seat.rid,
		seat_index = seat.index,
		is_ready = EBOOL.TRUE,
		table_state = 0,
	}
	noticemsg.table_state = table_data.state
	msghelper:sendmsg_to_alltableplayer("gamereadyN", noticemsg)
	
	if table_data.conf.match_type and request.rid > 100 and table_data.round > 1 then
		if table_data.playernum <= 2 then
			local match_type = table_data.conf.match_type
			if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
				skynet.call(".router","lua", "restart_matchtable", {request.rid}, match_type)
			elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
				skynet.call(".router","lua", "restart_coinmatchtable", {request.rid}, match_type)
			end
			TableLogic.passive_standuptable(table_data, seat)
		end
    end

	local isallready = true
	for k, v in ipairs(table_data.seats) do
		if v.state ~= ESeatState.SEAT_STATE_NO_PLAYER then
			if v.state ~= ESeatState.SEAT_STATE_WAIT_START then
				isallready = false
				break
			end
		end
	end
	filelog.debug("------ isallready ------", isallready)
	if isallready then
		TableLogic.startgame(table_data)
	end
end

function TableLogic.startgame(table_data, request)
	if TableLogic.is_canstartgame(table_data) then
		table_data.state = ETableState.TABLE_STATE_GAME_START
		table_data.gaming = true
		filelog.warn("------ game start ------")
		-- gamelogic.gamestart()
		table_data.timer_id = timer.settimer(3*100, "waitstartcountdown")
		return true
	else
		table_data.state = ETableState.TABLE_STATE_WAIT_ALL_READY
		return false
	end
end

function TableLogic.doaction(table_data, request, seat)
	if table_data.state ~= ETableState.TABLE_STATE_ACTION then return false end
	if request.action_type > EActionType.ACTION_TYPE_FOLD or request.action_type <= EActionType.ACTION_TYPE_BLIND_BET2 then
		return false
	end
	if seat.index ~= table_data.action_seat_index then
		return false
	end
	table_data.action_type = request.action_type
	if gamelogic.actions[request.action_type] then
		return gamelogic.actions[request.action_type](seat, request.action_param)
	end
	return false
end

function TableLogic.pay_money(table_data, seat)
	table_data.tmpdiamond = table_data.tmpdiamond or {}
	table_data.tmpdiamond[seat.rid] = {
		rolename = seat.playerinfo.rolename,
		beginvalue = math.floor(seat.startdiamond),
		value = math.floor(seat.diamond - seat.startdiamond),
		endvalue = math.floor(seat.diamond),
	}
end

function TableLogic.change_money(table_data, seat, value, winseat)
	if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
		if seat.diamond + value >= 0 then
			seat.diamond = seat.diamond + value
			seat.playerinfo.diamond = seat.playerinfo.diamond + value
		else
			seat.diamond = 0
			seat.playerinfo.diamond = 0
			filelog.sys_error("TableLogic change money error", seat.rid, seat.diamond, value)
		end
	elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
		if seat.diamond + seat.playerinfo.diamond + value >= 0 then
			seat.diamond = seat.diamond + value
			seat.playerinfo.coin = seat.playerinfo.coin + value
			if seat.diamond + value < 0 then
				seat.playerinfo.diamond = seat.playerinfo.diamond + (seat.diamond + value)
			end
		else
			seat.diamond = 0
			seat.playerinfo.coin = 0
			filelog.sys_error("TableLogic change money error", seat.rid, seat.diamond, value)
		end
	end
	if not winseat then
		seat.bets = seat.bets - value
		seat.curr_bets = seat.curr_bets - value
		table_data.allbets = table_data.allbets - value
	end
end

function TableLogic.save_actions(table_data, action, cards)
	local records = table_data.gamerecords
	if type(records) ~= type{} or not records.logname then
		filelog.sys_error("save actions error: no records", records, action, cards)
		return
	end
	local logstr = ''
	if not action then
		logstr = logstr
			.. (records.table_uuid or '0') .. '\n'
			.. table_data.conf.room_type .. '\n'
			.. (records.conf or "{}") .. '\n'
			.. (records.start_time or '0') .. '\n'
			.. (records.playerdata or '{}') .. '\n'
			.. (records.initcards or "{}") .. '\n'				-- 初始牌堆
			.. (records.handcards or "{}")
	else
		if type(action) == type{} then
			logstr = logstr .. json.encode(action)
		elseif action == "end" then
			logstr = logstr .. action
		else
			logstr = logstr .. action .. '_'
				.. "cards:" .. (json.encode(cards or {}))
		end
	end
	-- filelog.info("logstr = ", logstr)
	skynet.call(".tablereplaylog", "lua", "write", records.logname, logstr)
end

function TableLogic.change_all_player_money(table_data)
	if not table_data.tmpdiamond then
		filelog.sys_error("no data for changing player money,is somewhere error???")
		return
	end

	local records = table_data.gamerecords
	if type(records) ~= type{} or not records.logname then
		filelog.sys_error("change all player money error: no records")
		return
	end

	local commnet = {
		table_uuid = records.table_uuid,
		table_tradeid = skynet.call(".nodeuuid", "lua", "getuuid"),
		start_time = records.start_time or '0',
		replay_logname = records.logname,
	}

	filelog.trace("change all player money: ", table_data.tmpdiamond, commnet)
	local val = 0
	for k, v in pairs(table_data.tmpdiamond) do
		val = val + v.value
	end
	if val ~= 0 then
		filelog.error("diamond balance error!")
	end

	if table_data.conf.room_type == ERoomType.ROOM_TYPE_DIAMOND then
		msghelper:sendmsg_to_gatesvr("game_balance_diamond", table_data.tmpdiamond, commnet)
	elseif table_data.conf.room_type == ERoomType.ROOM_TYPE_COIN then
		msghelper:sendmsg_to_gatesvr("game_balance_coin_diamond", table_data.tmpdiamond, commnet)
	end
	-- TableLogic.save_gamerecords(table_data, commnet)
end

--根据指定桌位号获得一张空座位
function TableLogic.get_emptyseat_by_index(table_data)
	for index, seat in pairs(table_data.seats) do
		if seatlogic.is_empty(seat) then
			return seat
		end
	end
	return nil
end

function TableLogic.get_seat_by_rid(table_data, rid)
	for index, seat in pairs(table_data.seats) do
		if rid == seat.rid then
			return seat
		end
	end
	return nil
end

--判断桌子是否满了
function TableLogic.is_full(table_data)
	return table_data.sitdown_player_num >= table_data.conf.max_player_num
end

--判断当前是否能够开始游戏
function TableLogic.is_canstartgame(table_data)
	return table_data.sitdown_player_num >= table_data.conf.min_player_num
end

--判断当前局是否已经结束游戏
function TableLogic.is_onegameend(table_data)
	return (not table_data.gaming)
end

return TableLogic
